package MF1DSolver;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;

import jmeshless.NewOkCancelDialog;

import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

import syntaxanalyser.PostfiksX;
import FEMShapeFunction1D.FEMShapeFunction1D1;
import FEMSolver1D.MainMatrixGenerator1;
import FEMSolver1D.MatrixGenerator1;
import HashMatrix2.HashMatrix2;
import HashMatrix2.LUFactorization;
import Integration.IFunction1D;
import MeshlessShapeFunctions.JMeshlessInterpolationFunction1D;
import MeshlessShapeFunctions.JMeshlessInterpolationFunction1DCoupled2;

public class MF1DEnrichedCoupledSolver {
	
	HashMatrix2 mainMatrix;
	double[] b;
	ArrayList<FEMShapeFunction1D1> femList;
	ArrayList<enrichedFEM> mfList;
    ArrayList<Double> NodeList;

    String pString="1";
    String wString="0";
    String qString="10";
    String fString="100";

    double left;
    double right;

    boolean DirihetA;
    double AVal;
    boolean DirihetB;
    double BVal;
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		 ArrayList<Double> list = new ArrayList<Double>();
	        double a = 0.0;
	        double b = 1;
//	        int N = 20;
//	        double h = (b-a)/N;
//	        for (int i = 0; i <= N; i++) {
//	            list.add(a+h*i);
//	        }
//	        list.add(0.0);
//	        list.add(0.1);
////	        list.add(0.5);
//	        list.add(0.9);
//	        list.add(0.0);
	        list.add(0.0);
	        list.add(0.1);
	        list.add(0.2);
	        list.add(0.3);
	        list.add(0.4);
	        list.add(0.5);
	        
	        list.add(1.0);
		 double h = 0.1;
	        
	        ArrayList<FEMShapeFunction1D1> fem = new ArrayList<FEMShapeFunction1D1>();
	        ArrayList<Integer> pos = new ArrayList<Integer>();
	        for (int i = 0; i < list.size(); i++) {
	            fem.add(new FEMShapeFunction1D1(list.get(i), h));
	            pos.add(i);
	        }
	        ArrayList<enrichedFEM> mfList = new ArrayList<enrichedFEM>();
	        mfList.add(new enrichedFEM(5, fem, pos ));
//	        mfList.add(new enrichedFEMElement(10, new FEMShapeFunction1D1(list.get(15), h), new FEMShapeFunction1D1(list.get(16), h), 15, 16));
//	        mfList.add(new enrichedFEMElement(10, new FEMShapeFunction1D1(list.get(16), h), new FEMShapeFunction1D1(list.get(17), h), 16, 17));
//	        mfList.add(new enrichedFEMElement(10, new FEMShapeFunction1D1(list.get(17), h), new FEMShapeFunction1D1(list.get(18), h), 17, 18));
//	        mfList.add(new enrichedFEMElement(10, new FEMShapeFunction1D1(list.get(18), h), new FEMShapeFunction1D1(list.get(19), h), 18, 19));
//	        mfList.add(new enrichedFEMElement(10, new FEMShapeFunction1D1(list.get(19), h), new FEMShapeFunction1D1(list.get(20), h), 19, 20));
//	        mfList.add(new enrichedFEMElement(10, new FEMShapeFunction1D1(list.get(20), h), new FEMShapeFunction1D1(list.get(21), h), 20, 21));
	        
	        
	        MF1DEnrichedCoupledSolver gen = new MF1DEnrichedCoupledSolver(list,mfList,fem);

	        	gen.setLeft(a);
	            gen.setRight(b);
	            gen.setfString("10");
	            gen.setwString("20");
	            gen.setqString("0");
	            gen.setpString("1");
	            gen.setDirihetB(true);
	            gen.setBVal(0.0);
	            gen.setDirihetA(true);
	            gen.setAVal(0.0);
	            
	        gen.Generate();
	        gen.Solve("");

	}
    


    public MF1DEnrichedCoupledSolver(ArrayList<Double> NodeList,ArrayList<enrichedFEM> mfList,ArrayList<FEMShapeFunction1D1> fem) {
        this.NodeList = NodeList;
        this.mfList = mfList;
        this.femList = fem;
        int NNN = 0;
        for (int i = 0; i < mfList.size(); i++) {
			NNN+=mfList.get(i).mfList.size();
		}
        mainMatrix = new HashMatrix2(HashMatrix2.ARRAY_TYPE, NodeList.size()+NNN);
        b = new double[NodeList.size()+NNN];
//        generateFunctions();

    }

    @SuppressWarnings("unused")
	private void generateFunctions(){
        femList = new ArrayList<FEMShapeFunction1D1>();
        double h = NodeList.get(1) - NodeList.get(0);
        for (int i = 0; i < NodeList.size(); i++) {
            femList.add(new FEMShapeFunction1D1(NodeList.get(i), h));
        }
    }

    public void Generate() {
        MatrixGenerator1 gen = new MatrixGenerator1(NodeList.get(0),NodeList.get(NodeList.size()-1));

        gen.setLeft(0);
        gen.setRight(1);

        IFunction1D p = new IFunction1D() {

            public double calculate(double x) {
                try {
                    return PostfiksX.Calculate(pString, x);
                } catch (Exception ex) {
                    Logger.getLogger(MainMatrixGenerator1.class.getName()).log(Level.SEVERE, null, ex);
                }
                return 0;
            }

            public double calculateDerivaty(double x) {
                throw new UnsupportedOperationException("Not supported yet.");
            }
        };
        IFunction1D w = new IFunction1D() {

            public double calculate(double x) {
                try {
                    return PostfiksX.Calculate(wString, x);
                } catch (Exception ex) {
                    Logger.getLogger(MainMatrixGenerator1.class.getName()).log(Level.SEVERE, null, ex);
                }
                return 0;
            }

            public double calculateDerivaty(double x) {
                throw new UnsupportedOperationException("Not supported yet.");
            }
        };
        IFunction1D q = new IFunction1D() {

            public double calculate(double x) {
                try {
                    return PostfiksX.Calculate(qString, x);
                } catch (Exception ex) {
                    Logger.getLogger(MainMatrixGenerator1.class.getName()).log(Level.SEVERE, null, ex);
                }
                return 0;
            }

            public double calculateDerivaty(double x) {
                throw new UnsupportedOperationException("Not supported yet.");
            }
        };
        IFunction1D f = new IFunction1D() {

            public double calculate(double x) {
                try {
                    return PostfiksX.Calculate(fString, x);
                } catch (Exception ex) {
                    Logger.getLogger(MainMatrixGenerator1.class.getName()).log(Level.SEVERE, null, ex);
                }
                return 0;
            }

            public double calculateDerivaty(double x) {
                throw new UnsupportedOperationException("Not supported yet.");
            }
        };

        
        // generating FEM influence of approximation
        for (int i = 0; i <NodeList.size(); i++) {
            //IFunction1D f1 = FuncList.get(i);
            gen.setF1(femList.get(i));
            gen.setF2(femList.get(i));
            //mainMatrix.setValue(i, i, gen.getKe(p)+gen.getAdv(w)+gen.getMe(q));
            for (int j = 0; j < NodeList.size(); j++) {
                gen.setF2(femList.get(j));
                double val = gen.getKe(p)+gen.getAdv(w)+gen.getMe(q);
                mainMatrix.setValue(i, j, val);
            }
            b[i] = gen.getFe(f);
        }
        // generating MF influence of approximation
        int NNN = NodeList.size();
        for (Iterator<enrichedFEM> iterator = this.mfList.iterator(); iterator.hasNext();) {
			enrichedFEM elem = iterator.next();
			elem.setF(f);
			elem.setP(p);
			elem.setQ(q);
			elem.setW(w);
			double[][] tmp = elem.generateMatrix();
			double[] tmpVec = elem.generateFVector();
			for (int i = 0; i < tmp.length; i++) {
				for (int j = 0; j < tmp.length; j++) {
					mainMatrix.setValue(NNN + i, NNN + j, tmp[i][j]);
				}
				b[i + NNN] = tmpVec[i];
			}
			tmp = elem.generateMFFEM();
			
			for (int i = 0; i < elem.fem.size(); i++) {
				int pos = elem.getFEMPos(i);
				for (int j = 0; j < elem.mfList.size(); j++) {
					mainMatrix.setValue(pos, NNN + j, tmp[i][j]);
				}
				
			}
			
			tmp = elem.generateFEMMF();
			
			for (int i = 0; i < elem.fem.size(); i++) {
				int pos = elem.getFEMPos(i);
				for (int j = 0; j < elem.mfList.size(); j++) {
					mainMatrix.setValue(NNN + j, pos, tmp[j][i]);
				}
			}			
			NNN+=elem.mfList.size();			
		}
        
//        //print main matrix:
        for (int i = 0; i < b.length; i++) {
        	for (int j = 0; j < b.length; j++) {
    			System.out.print(mainMatrix.getValue(i, j)+"\t");
    		}
//        	System.out.println("\t\t\t\t||\t\t"+b[i]);
//        	System.out.println(b[i]);
        	System.out.println();
		}
        System.out.println("---------------------------------------------------------------------------------");
        
        
    }

    public void applyBounderiesForCoupling(){
    	if (DirihetA) {
        	for (int i = 0; i < b.length; i++) {
        		b[i]-= AVal*mainMatrix.getValue(i, 0);
			}
            b[0] = AVal;
            
            for (int i = 0; i < b.length; i++) {
            	mainMatrix.setValue(0, i, 0);
                mainMatrix.setValue(i, 0, 0);
			}
            
            mainMatrix.setValue(0, 0, 1);
            
        }else{
            b[0]+=AVal;
        }
        

        //Dirihlet b:
        if (DirihetB) {
        	int N = NodeList.size();
        	for (int i = 0; i < b.length; i++) {
        		b[i]-= BVal*mainMatrix.getValue(i, N-1);
			}
            b[N-1] = BVal;
            
            for (int i = 0; i < b.length; i++) {
            	mainMatrix.setValue(N-1, i, 0);
                mainMatrix.setValue(i, N-1, 0);
			}
            
            mainMatrix.setValue(N-1, N-1, 1);
            
        }else{
            b[NodeList.size()-1]+=BVal;
        }
        
      //print main matrix:
        for (int i = 0; i < b.length; i++) {
        	for (int j = 0; j < b.length; j++) {
    			System.out.print(mainMatrix.getValue(i, j)+"\t");
    		}
//        	System.out.println("\t\t\t\t||\t\t"+b[i]);
//        	System.out.println(b[i]);
        	System.out.println();
		}
    }


    public double[] Solve(String outPath){
        this.applyBounderiesForCoupling();
        LUFactorization lu = new LUFactorization(mainMatrix);
        double[] res = lu.calculate(b);
        for (int j = 0; j < NodeList.size(); j++) {
//          System.out.println(NodeList.get(j)+"\t"+res[j]);
      }
        System.out.println();
        System.out.println("----------------------------------------");
        System.out.println();
        for (int j = 0; j < res.length; j++) {
          System.out.println(res[j]);
      }
        System.out.println();
        System.out.println("----------------------------------------");
        System.out.println();
        System.out.println();
        ArrayList<JMeshlessInterpolationFunction1DCoupled2> l = new ArrayList<JMeshlessInterpolationFunction1DCoupled2>();
        for (int i = 0; i < mfList.size(); i++) {
        	l.addAll(this.mfList.get(i).mfList);
		}
        double h = 0.01;
        XYSeries series1 = new XYSeries("Стабілізований розвязок методом штрафу", true, true);
        for ( double x = 0.0 ; x <= 1 + h; x+=h) {
            double tmp = 0.0;
            for (int j = 0; j <l.size(); j++) {
                tmp+= l.get(j).calculate(x)*res[j+NodeList.size()];
            }
            if (Double.isNaN(tmp)) {
				tmp = 0.0;
			}
            for (int i = 0; i < femList.size(); i++) {
				tmp+=femList.get(i).calculate(x)*res[i];
			}
            System.out.print(x+"\t"+tmp+"\n");
            series1.add(x,tmp);
        }

        XYSeriesCollection col = new XYSeriesCollection(series1);
//        col.addSeries(series2);
//        col.addSeries(series3);

        NewOkCancelDialog dialog = new NewOkCancelDialog(null, false, col);
        dialog.setLocationRelativeTo(null);
        //dialog.setUndecorated(true);
        dialog.setVisible(true);
        
        
        
//        for (int i = 0; i < l.size(); i++) {
//            JMeshlessInterpolationFunction1DCoupled2 f = l.get(i);
//            double x=0.0;
//            System.out.println("f([");
//            while(x<1.0+0.01){
//                System.out.println(x+"\t\t"+f.calculateDiffuseDerivaty(x));
//                x+=0.01;
//            }
//            System.out.println("]);");
//        }
        
        
        
        return res;
    }

    public void setAVal(double AVal) {
        this.AVal = AVal;
    }

    public void setBVal(double BVal) {
        this.BVal = BVal;
    }

    public void setDirihetA(boolean DirihetA) {
        this.DirihetA = DirihetA;
    }

    public void setDirihetB(boolean DirihetB) {
        this.DirihetB = DirihetB;
    }

    public void setfString(String fString) {
        this.fString = fString;
    }

    public void setpString(String pString) {
        this.pString = pString;
    }

    public void setqString(String qString) {
        this.qString = qString;
    }

    public void setwString(String wString) {
        this.wString = wString;
    }

    public void setLeft(double left) {
        this.left = left;
    }

    public void setRight(double right) {
        this.right = right;
    }

}
